/*
Copyright [2020] [https://www.xiaonuo.vip]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Snowy采用APACHE LICENSE 2.0开源协议，您在使用过程中，需要注意以下几点：

1.请不要删除和修改根目录下的LICENSE文件。
2.请不要删除和修改Snowy源码头部的版权声明。
3.请保留源码和相关描述文件的项目出处，作者声明等。
4.分发源码时候，请注明软件出处 https://gitee.com/xiaonuobase/snowy
5.在修改包名，模块名称，项目代码等时，请注明软件出处 https://gitee.com/xiaonuobase/snowy
6.若您的项目无法满足以上几点，可申请商业授权，获取Snowy商业授权许可，请在官网购买授权，地址为 https://www.xiaonuo.vip
 */
package vip.xiaonuo.sys.modular.courseschedule.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import vip.xiaonuo.core.context.login.LoginContextHolder;
import vip.xiaonuo.core.exception.ServiceException;
import vip.xiaonuo.core.factory.PageFactory;
import vip.xiaonuo.core.pojo.page.PageResult;
import vip.xiaonuo.core.util.PoiUtil;
import vip.xiaonuo.sys.modular.coursepublish.entity.CoursePublish;
import vip.xiaonuo.sys.modular.coursepublish.service.CoursePublishService;
import vip.xiaonuo.sys.modular.courseschedule.entity.CourseSchedule;
import vip.xiaonuo.sys.modular.courseschedule.enums.CourseScheduleExceptionEnum;
import vip.xiaonuo.sys.modular.courseschedule.mapper.CourseScheduleMapper;
import vip.xiaonuo.sys.modular.courseschedule.param.CourseScheduleParam;
import vip.xiaonuo.sys.modular.courseschedule.param.CourseScheduleParamForQuery;
import vip.xiaonuo.sys.modular.courseschedule.param.CourseScheduleParamForSaving;
import vip.xiaonuo.sys.modular.courseschedule.result.CourseScheduleResult;
import vip.xiaonuo.sys.modular.courseschedule.service.CourseScheduleService;
import vip.xiaonuo.sys.modular.courseschedulefrequency.entity.CourseScheduleFrequency;
import vip.xiaonuo.sys.modular.courseschedulefrequency.param.CourseScheduleFrequencyParam;
import vip.xiaonuo.sys.modular.courseschedulefrequency.service.CourseScheduleFrequencyService;
import vip.xiaonuo.sys.modular.courseselection.dto.CourseSelectionQryDto;
import vip.xiaonuo.sys.modular.courseselection.entity.CourseSelection;
import vip.xiaonuo.sys.modular.courseselection.mapper.CourseSelectionMapper;
import vip.xiaonuo.sys.modular.courseselection.service.CourseSelectionCheckService;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 排课service接口实现类
 *
 * @author kongxm
 * @date 2022-07-11 11:31:12
 */
@Service
public class CourseScheduleServiceImpl extends ServiceImpl<CourseScheduleMapper, CourseSchedule> implements CourseScheduleService {
    @Autowired
    private CoursePublishService coursePublishService;
    @Autowired
    private CourseScheduleFrequencyService courseScheduleFrequencyService;
    @Autowired
    private CourseSelectionMapper courseSelectionMapper;
    @Autowired
    private CourseSelectionCheckService courseSelectionCheckService;

    @Override
    public PageResult<CourseSchedule> page(CourseScheduleParam courseScheduleParam) {
        QueryWrapper<CourseSchedule> queryWrapper = new QueryWrapper<>();
        if (ObjectUtil.isNotNull(courseScheduleParam)) {

            // 根据开课id 查询
            if (ObjectUtil.isNotEmpty(courseScheduleParam.getCoursePublishId())) {
                queryWrapper.lambda().eq(CourseSchedule::getCoursePublishId, courseScheduleParam.getCoursePublishId());
            }
            // 根据项目id 查询
            if (ObjectUtil.isNotEmpty(courseScheduleParam.getProjectId())) {
                queryWrapper.lambda().eq(CourseSchedule::getProjectId, courseScheduleParam.getProjectId());
            }
            // 根据模块id 查询
            if (ObjectUtil.isNotEmpty(courseScheduleParam.getModuleId())) {
                queryWrapper.lambda().eq(CourseSchedule::getModuleId, courseScheduleParam.getModuleId());
            }
            // 根据老师id 查询
            if (ObjectUtil.isNotEmpty(courseScheduleParam.getTeacherId())) {
                queryWrapper.lambda().eq(CourseSchedule::getTeacherId, courseScheduleParam.getTeacherId());
            }
            // 根据教室id 查询
            if (ObjectUtil.isNotEmpty(courseScheduleParam.getClassroomId())) {
                queryWrapper.lambda().eq(CourseSchedule::getClassroomId, courseScheduleParam.getClassroomId());
            }
            // 根据最大人数 查询
            if (ObjectUtil.isNotEmpty(courseScheduleParam.getMaxStudents())) {
                queryWrapper.lambda().eq(CourseSchedule::getMaxStudents, courseScheduleParam.getMaxStudents());
            }
        }
        return new PageResult<>(this.page(PageFactory.defaultPage(), queryWrapper));
    }

    @Override
    public PageResult<CourseScheduleResult> pageWithName(CourseScheduleParamForQuery courseScheduleParamForQuery) {
        if (LoginContextHolder.me().isSuperAdmin()) { //系统管理员：查看所有数据
            // don't filter data
            log.debug("系统管理员：查看所有数据");
        } else if (LoginContextHolder.me().hasRole("ca")) { //院系管理员：查看本院系的数据
            Long collegeId = LoginContextHolder.me().getSysLoginUser().getCollId();
            courseScheduleParamForQuery.setCollegeId(collegeId);
        } else {//教师：没有本页面的查看权限。
            return new PageResult<>();
        }
        Page<CourseScheduleResult> page = this.baseMapper.
                pageWithName(PageFactory.defaultPage(), courseScheduleParamForQuery);
        page.getRecords().forEach(item -> {
            String dayOfWeek = item.getDayOfWeek();
            if (StringUtils.isNotEmpty(dayOfWeek)) {
                String newDayOfWeek = Arrays.stream(dayOfWeek.split(","))
                        .distinct().collect(Collectors.joining(","));
                item.setDayOfWeek(newDayOfWeek);
            }
        });
        return new PageResult<>(page);
    }

    @Override
    public List<CourseSchedule> list(CourseScheduleParam courseScheduleParam) {
        return this.list();
    }

    @Override
    public void add(CourseScheduleParam courseScheduleParam) {
        CourseSchedule courseSchedule = new CourseSchedule();
        BeanUtil.copyProperties(courseScheduleParam, courseSchedule);
        this.save(courseSchedule);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(CourseScheduleParamForSaving courseScheduleParamForSaving) {
        CoursePublish coursePublish = coursePublishService.getById(courseScheduleParamForSaving.getCoursePublishId());
        assert coursePublish != null;
        CourseSchedule courseSchedule = new CourseSchedule();
        BeanUtil.copyProperties(courseScheduleParamForSaving, courseSchedule);
        courseSchedule.setCourseId(coursePublish.getCourseId());
        courseSchedule.setState(1);
        this.save(courseSchedule);

        // getting the ID of the inserted coursePublish and then assign it to subItems
        courseScheduleParamForSaving.getFrequencyList().forEach(item -> {
            item.setCourseScheduleId(courseSchedule.getId());
            item.setCourseId(courseSchedule.getCourseId());
            courseScheduleFrequencyService.add(item);
        });
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(List<CourseScheduleParam> courseScheduleParamList) {
        courseScheduleParamList.forEach(courseScheduleParam -> {

            CourseSchedule info = this.getById(courseScheduleParam.getId());
            if(info==null){
                throw new ServiceException(-1, "排课信息不存在");
            }
            //排课置为删除
            CourseSchedule courseSchedule = new CourseSchedule();
            courseSchedule.setId(courseScheduleParam.getId());
            courseSchedule.setStatus(2);
            this.updateById(courseSchedule);
            //排课对应选课置为删除
            UpdateWrapper<CourseSelection> selectionUpdateWrapper = new UpdateWrapper();
            selectionUpdateWrapper.eq("course_publish_id",info.getCoursePublishId());
            selectionUpdateWrapper.eq("course_schedule_id",info.getId());
            CourseSelection selection = new CourseSelection();
            selection.setStatus(2);
            courseSelectionMapper.update(selection,selectionUpdateWrapper);

        });
        //给对应开课的学生自动校验
        if(!CollectionUtils.isEmpty(courseScheduleParamList)){
            CourseSchedule courseSchedule = this.getById(courseScheduleParamList.get(0).getId());
            List<Long> stuList = courseSelectionMapper.getStuListByCoursePublishId(courseSchedule.getCoursePublishId());
            for(Long stuId:stuList){
                CourseSelectionQryDto selectionQryDto = new CourseSelectionQryDto();
                selectionQryDto.setUserId(stuId);
                selectionQryDto.setCoursePublishId(courseSchedule.getCoursePublishId());
                courseSelectionCheckService.getResult(selectionQryDto);
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void edit(CourseScheduleParam courseScheduleParam) {
        CourseSchedule courseSchedule = this.queryCourseSchedule(courseScheduleParam);
        BeanUtil.copyProperties(courseScheduleParam, courseSchedule);
        this.updateById(courseSchedule);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void edit(CourseScheduleParamForSaving courseScheduleParamForSaving) {
        CoursePublish coursePublish = coursePublishService.getById(courseScheduleParamForSaving.getCoursePublishId());
        assert coursePublish != null;

        //开课课程状态为“已确认”后，其所属的排课课程不可以再“编辑”和“撤回”。你们看呢？from 李晓庆
        if(coursePublish.getConfirmState() == 4) {
            throw new ServiceException(-1, "选课已确认，无法修改");
        }

        CourseSchedule courseSchedule = this.queryCourseSchedule(courseScheduleParamForSaving);
        if (courseSchedule.getState() == 1) { //开课课程一旦发布，除“实验人数“外其他信息不可编辑。
            courseSchedule.setMaxStudents(courseScheduleParamForSaving.getMaxStudents());
            this.updateById(courseSchedule);
            // 提前结束掉
            return;
        } else {
            BeanUtil.copyProperties(courseScheduleParamForSaving, courseSchedule);
            courseSchedule.setCourseId(coursePublish.getCourseId());
            this.updateById(courseSchedule);
        }
        //find the ids in the database which doesn't exist in the request params then remove them
        List<CourseScheduleFrequency> coursePublishFrequencyListExistInDatabase =
                courseScheduleFrequencyService.list(new LambdaQueryWrapper<CourseScheduleFrequency>()
                        .eq(CourseScheduleFrequency::getCourseScheduleId, courseScheduleParamForSaving.getId()));

        List<Long> idsByDeleteFrequency = coursePublishFrequencyListExistInDatabase.stream()
                .filter(item -> courseScheduleParamForSaving.getFrequencyList()
                        .stream().noneMatch(subItem -> subItem.getId() != null && subItem.getId().equals(item.getId())))
                .map(CourseScheduleFrequency::getId).collect(Collectors.toList());
        courseScheduleFrequencyService.removeByIds(idsByDeleteFrequency);

        //add the frequency list,when the method is update, the frontend must pass the id to backend otherwise it will be inserted.
        for (CourseScheduleFrequencyParam item : courseScheduleParamForSaving.getFrequencyList()) {
            item.setCourseScheduleId(courseSchedule.getId());
            item.setCourseId(courseSchedule.getCourseId());
            CourseScheduleFrequency entity = new CourseScheduleFrequency();
            BeanUtil.copyProperties(item, entity);
            courseScheduleFrequencyService.saveOrUpdate(entity);
        }
    }

    @Override
    public CourseSchedule detail(CourseScheduleParam courseScheduleParam) {
        return this.queryCourseSchedule(courseScheduleParam);
    }

    /**
     * 获取排课
     *
     * @author kongxm
     * @date 2022-07-11 11:31:12
     */
    private CourseSchedule queryCourseSchedule(CourseScheduleParam courseScheduleParam) {
        CourseSchedule courseSchedule = this.getById(courseScheduleParam.getId());
        if (ObjectUtil.isNull(courseSchedule)) {
            throw new ServiceException(CourseScheduleExceptionEnum.NOT_EXIST);
        }
        return courseSchedule;
    }

    @Override
    public void export(CourseScheduleParam courseScheduleParam) {
        List<CourseSchedule> list = this.list(courseScheduleParam);
        PoiUtil.exportExcelWithStream("SnowyCourseSchedule.xls", CourseSchedule.class, list);
    }


    @Override
    public void publish(Long id) {
        //judgement if the record is exist
        CourseSchedule persistence = getById(id);
        if (ObjectUtil.isNull(persistence)) {
            throw new ServiceException(CourseScheduleExceptionEnum.NOT_EXIST);
        }
        // only can publish the record that it's publishState is not published
        if (persistence.getState() == 1) {
            throw new ServiceException(CourseScheduleExceptionEnum.PUBLISHED);
        }
        CourseSchedule courseSchedule = new CourseSchedule();
        BeanUtil.copyProperties(persistence, courseSchedule);
        courseSchedule.setId(id);
        courseSchedule.setState(1);
        updateById(courseSchedule);
    }

    @Override
    public void rollback(Long id) {
//        3, 当开课状态是“已发布”时，显示按钮“撤回”，点击“撤回”按钮，修改开课课程的状态为“待发布”。
        //judgement if the record is exist
        CourseSchedule persistence = getById(id);
        if (ObjectUtil.isNull(persistence)) {
            throw new ServiceException(CourseScheduleExceptionEnum.NOT_EXIST);
        }
        // only can rollback the record that it's publishState is published
        if (persistence.getState() == 0) {
            throw new ServiceException(CourseScheduleExceptionEnum.NOT_PUBLISHED);
        }

        CoursePublish coursePublish = coursePublishService.getById(persistence.getCoursePublishId());
        assert coursePublish != null;

        //开课课程状态为“已确认”后，其所属的排课课程不可以再“编辑”和“撤回”。你们看呢？from 李晓庆
        if(coursePublish.getConfirmState() == 4) {
            throw new ServiceException(-1, "选课已确认，无法撤回");
        }

        CourseSchedule courseSchedule = new CourseSchedule();
        BeanUtil.copyProperties(persistence, courseSchedule);
        courseSchedule.setId(id);
        courseSchedule.setState(0);
        updateById(courseSchedule);
    }


}
